package ntut.andengine.game;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import ntut.andengine.events.Command;
import ntut.andengine.events.UpdateCommand;
import ntut.andengine.events.UpdateEmitter;
import ntut.andengine.physics.Box;
import ntut.andengine.physics.ContactEvent;
import ntut.andengine.physics.PauseablePhysicsWorld;
import ntut.andengine.physics.PhysicalComponent;
import ntut.andengine.physics.PhysicalComponent.ComponentColor;
import ntut.andengine.physics.Player;

import org.anddev.andengine.audio.music.Music;
import org.anddev.andengine.audio.music.MusicFactory;
import org.anddev.andengine.audio.sound.Sound;
import org.anddev.andengine.audio.sound.SoundFactory;
import org.anddev.andengine.engine.handler.IUpdateHandler;
import org.anddev.andengine.entity.primitive.Rectangle;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.shape.Shape;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.extension.physics.box2d.PhysicsFactory;
import org.anddev.andengine.extension.physics.box2d.util.Vector2Pool;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.bitmap.BitmapTexture;
import org.anddev.andengine.opengl.texture.bitmap.BitmapTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.sensor.accelerometer.AccelerometerData;
import org.anddev.andengine.sensor.accelerometer.IAccelerometerListener;
import org.anddev.andengine.util.Debug;

import android.util.Log;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Manifold;

public class GameLevel extends GameScene implements IAccelerometerListener,
		IOnSceneTouchListener {

	public static final short CATEGORYBIT_WHITE = 0x01 << 1;
	public static final short CATEGORYBIT_RED = 0x01 << 2;
	public static final short CATEGORYBIT_BLUE = 0x01 << 3;

	public static final short MASKBITS_WHITE = CATEGORYBIT_WHITE
			+ CATEGORYBIT_RED + CATEGORYBIT_BLUE;
	public static final short MASKBITS_RED = CATEGORYBIT_WHITE
			+ CATEGORYBIT_RED;
	public static final short MASKBITS_BLUE = CATEGORYBIT_WHITE
			+ CATEGORYBIT_BLUE;

	private static final int CAMERA_WIDTH = 480;
	private static final int CAMERA_HEIGHT = 800;

	PauseablePhysicsWorld mPhysicsWorld;
	GameLevel mLevel;
	Player mSparkle;
	List<UpdateEmitter> sparkleListeners;
	List<Command> commandList;
	List<UpdateCommand> updateCommandList;
	List<PhysicalComponent> mPhysicalComponentList;
	List<PhysicalComponent> movingComponentList;
    List<Vector2> movingComponentPositionList;
    List<Box> deathBoxList;
    List<Box> deathBoxPositionList;
    private TextureRegion mPausedTextureRegion;
    private TextureRegion mPausedButtonTextureRegion;
    protected Music mMusic;
    protected Sound successSound;

	@Override
	public void onLoadResources() {
		super.onLoadResources();

		mLevel = this;

		sparkleListeners = new ArrayList<UpdateEmitter>();
		commandList = new ArrayList<Command>();
		updateCommandList = new ArrayList<UpdateCommand>();
		mPhysicalComponentList = new ArrayList<PhysicalComponent>();
		movingComponentList = new ArrayList<PhysicalComponent>();
		movingComponentPositionList = new ArrayList<Vector2>();
		deathBoxList = new ArrayList<Box>();
		deathBoxPositionList = new ArrayList<Box>();
		
		MusicFactory.setAssetBasePath("mfx/");
		try {
			this.mMusic = MusicFactory.createMusicFromAsset(
					this.mEngine.getMusicManager(), this, "music.ogg");
			this.mMusic.setLooping(true);
		} catch (final IOException e) {
			Debug.e(e);
		}
		SoundFactory.setAssetBasePath("mfx/");
		try {
			this.successSound = SoundFactory.createSoundFromAsset(
					this.mEngine.getSoundManager(), this,
					"wagner_the_ride_of_the_valkyries.ogg");
		} catch (final IOException e) {
			Debug.e(e);
		}

		setPhysicalWorld();
		setBorder();

		mScene.setOnSceneTouchListener(this);

		/* UPDATE */
		mScene.registerUpdateHandler(new IUpdateHandler() {

			@Override
			public void onUpdate(float pSecondsElapsed) {
				updateEmitter();
				exexuteCommand();
				updateCommand(pSecondsElapsed);
				memorizeBox();
			}

			@Override
			public void reset() {}
			
		});

		addCommand(new Command() {

			@Override
			public void executeCommand() {
				memorizePosition();
				
			}
		});	

	}

	@Override
	public void onLoadComplete() {
		super.onLoadComplete();
		setPauseButton();
		mMusic.play();
	}

	private void setPauseButton() {
		BitmapTexture bitmapTexture = new BitmapTexture(256, 128,
				TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		this.mPausedTextureRegion = BitmapTextureRegionFactory.createFromAsset(
				bitmapTexture, this, "paused.png", 0, 0);
		this.mPausedButtonTextureRegion = BitmapTextureRegionFactory
				.createFromAsset(bitmapTexture, this, "pause_button.png", 0, 50);
		mEngine.getTextureManager().loadTexture(bitmapTexture);

		final int x = CAMERA_WIDTH / 2 - this.mPausedTextureRegion.getWidth()
				/ 2;
		final int y = CAMERA_HEIGHT / 2 - this.mPausedTextureRegion.getHeight()
				/ 2;
		final Sprite pausedSprite = new Sprite(x, y, this.mPausedTextureRegion);
		final Sprite pausedButton = new Sprite(415, 5,
				this.mPausedButtonTextureRegion) {

			@Override
			public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
					float pTouchAreaLocalX, float pTouchAreaLocalY) {

				if (pSceneTouchEvent.isActionDown()) {
					if (!mPhysicsWorld.isPaused()) {
						mScene.attachChild(pausedSprite);
						mPhysicsWorld.setPaused(true);
						mScene.setOnSceneTouchListener(null);
						mMusic.pause();
					} else {
						mScene.detachChild(pausedSprite);
						mPhysicsWorld.setPaused(false);
						mScene.setOnSceneTouchListener(mLevel);
						mMusic.play();
					}
					return true;
				}
				return false;
			}
		};
		// pausedButton.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
		mScene.attachChild(pausedButton);
		mScene.registerTouchArea(pausedButton);
	}

	// SensorManager.GRAVITY_EARTH
	private void setPhysicalWorld() {
		this.mPhysicsWorld = new PauseablePhysicsWorld(new Vector2(0, 0), false);

		mPhysicsWorld.setContactListener(new ContactListener() {

			@Override
			public void beginContact(Contact contact) {

				Body A = contact.getFixtureA().getBody();
				Body B = contact.getFixtureB().getBody();

				if (A.getUserData() instanceof ContactEvent) {
					((ContactEvent) (A.getUserData())).beginContact(B, contact
							.getWorldManifold().getNormal());
				}

				if (B.getUserData() instanceof ContactEvent) {
					((ContactEvent) (B.getUserData())).beginContact(A, contact
							.getWorldManifold().getNormal().mul(-1));
				}
			}

			@Override
			public void endContact(Contact contact) {

				Body A = contact.getFixtureA().getBody();
				Body B = contact.getFixtureB().getBody();

				if (A.getUserData() instanceof ContactEvent) {
					((ContactEvent) (A.getUserData())).endContact(B, contact
							.getWorldManifold().getNormal());
				}

				if (B.getUserData() instanceof ContactEvent) {
					((ContactEvent) (B.getUserData())).endContact(A, contact
							.getWorldManifold().getNormal().mul(-1));
				}
			}

			@Override
			public void preSolve(Contact contact, Manifold oldManifold) {

			}

			@Override
			public void postSolve(Contact contact, ContactImpulse impulse) {

			}
		});
	}

	@Override
	public void onResumeGame() {
		super.onResumeGame();

		this.enableAccelerometerSensor(this);
	}

	@Override
	public void onPauseGame() {
		super.onPauseGame();

		this.disableAccelerometerSensor();
	}

	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {

		if (pSceneTouchEvent.isActionDown()) {
			MainCharacterChangeColor();
			return true;
		}
		return false;
	}

	@Override
	public void onAccelerometerChanged(AccelerometerData pAccelerometerData) {
		final Vector2 gravity = Vector2Pool.obtain(pAccelerometerData.getX(),
				pAccelerometerData.getY());
		if (mSparkle != null)
			mSparkle.setVelocity(gravity);
		Vector2Pool.recycle(gravity);
	}

	private void setBorder() {
		final Shape ground = new Rectangle(0, CAMERA_HEIGHT - 2, CAMERA_WIDTH,
				2);
		final Shape roof = new Rectangle(0, 0, CAMERA_WIDTH, 2);
		final Shape left = new Rectangle(0, 0, 2, CAMERA_HEIGHT);
		final Shape right = new Rectangle(CAMERA_WIDTH - 2, 0, 2, CAMERA_HEIGHT);

		final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0,
				0, false, CATEGORYBIT_WHITE, MASKBITS_WHITE, (short) 0);
		PhysicsFactory.createBoxBody(this.mPhysicsWorld, ground,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.mPhysicsWorld, roof,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.mPhysicsWorld, left,
				BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.mPhysicsWorld, right,
				BodyType.StaticBody, wallFixtureDef);

		mScene = new Scene();
		mScene.attachChild(ground);
		mScene.attachChild(roof);
		mScene.attachChild(left);
		mScene.attachChild(right);
	}

	public PauseablePhysicsWorld getPhysicsWorld(){
		return mPhysicsWorld;
	}

	public List<PhysicalComponent> getPhysicalComponentList() {
		return mPhysicalComponentList;
	}

	public void addColorComponent(PhysicalComponent component) {
		mPhysicalComponentList.add(component);
	}

	public void addMovingComponent(PhysicalComponent movingComponent) {
		movingComponentList.add(movingComponent);
	}

	public void addDeathBoxList(Box box){
		deathBoxList.add(box);
	}
	
	private void memorizePosition() {
		for (PhysicalComponent component : movingComponentList) {
			movingComponentPositionList.add(component.getPosition());
		}
	}

    public void recoveryPosition() {
        for (int i = 0; i < movingComponentPositionList.size(); i++) 
        {
            movingComponentList.get(i).setPosition(movingComponentPositionList.get(i));
            movingComponentList.get(i).setVelocity(new Vector2(0,0));
        }
    }
    
    public void memorizeBox() {
    	
    	for(Box box : deathBoxList){
    		//Box newBox = box;
    		//newBox.setVanish(false);
    		deathBoxPositionList.add(box);//
    	}
	}
    
    public void recoveryBox(PauseablePhysicsWorld mPhysicsWorld){
    	for (int i = 0; i < deathBoxList.size(); i++){
    		
    		if(deathBoxList.get(i).getVanish()){
    			Log.d("Box", "vanish!");
//    			deathBoxList.get(i).setPosition(deathBoxPositionList.get(i));
//    			deathBoxList.get(i).setVanish(false);
//    			Log.d("Box", deathBoxList.get(i).getPosition().toString());
    			
    			deathBoxList.get(i).recovery(mPhysicsWorld);//
    			deathBoxList.remove(i);
//    			deathBoxPositionList.get(i).setVanish(false);
//    			deathBoxPositionList.get(i).isVanish(mPhysicsWorld);
    			deathBoxPositionList.remove(i);
    			memorizeBox();
    			//deathBoxPositionList.get(i).setPosition(new Vector2(-100, -100));
    		}
    	}
    	//memorizeBox();
    }
	
	public void addSparkleListener(UpdateEmitter listener) {
		sparkleListeners.add(listener);
	}

	public void updateEmitter() {
		for (UpdateEmitter sparkleListener : sparkleListeners) {
			sparkleListener.onUpdateEmitterEvent();
		}
	}

	public void addCommand(Command command) {
		commandList.add(command);
	}

	public void exexuteCommand() {
		for (Command command : commandList) {
			command.executeCommand();
		}
		commandList.clear();
	}

	public void addUpdateCommand(UpdateCommand updateCommand) {
		updateCommandList.add(updateCommand);
	}

	public void updateCommand(float pSecondsElapsed) {
		for (UpdateCommand updateCommand : updateCommandList) {
			updateCommand.updateCommand(pSecondsElapsed);
		}
	}

	private void MainCharacterChangeColor() {

		if (mSparkle.getColor() == ComponentColor.Red) {
			mSparkle.changeColor(ComponentColor.Blue);
		} else if (mSparkle.getColor() == ComponentColor.Blue) {
			mSparkle.changeColor(ComponentColor.Red);
		}
	}

}
